package cn.murky.admin.system.biz.service.impl;

import cn.murky.admin.system.api.enums.I18nTag;
import cn.murky.admin.system.biz.domain.dto.SysI18nFromDTO;
import cn.murky.admin.system.biz.domain.vo.SysI18nVO;
import cn.murky.admin.system.biz.mq.SysI18nMqTemplate;
import cn.murky.admin.system.biz.service.ISysI18nService;
import cn.murky.admin.system.biz.service.ISysDictDataService;
import cn.murky.admin.system.biz.domain.dto.SysI18nDTO;
import cn.murky.admin.system.biz.domain.entity.SysI18n;
import cn.murky.admin.system.biz.domain.query.SysI18nPageQuery;
import cn.murky.admin.system.biz.mapper.SysI18nMapper;
import cn.murky.common.domain.bo.SysDictDataBO;
import cn.murky.common.utils.CollectionUtils;
import cn.murky.core.exception.ServiceException;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.solon.service.impl.ServiceImpl;
import org.noear.redisx.RedisClient;
import org.noear.redisx.plus.RedisHash;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Tran;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static cn.murky.common.constant.DictContant.I18N_TAG_DICT_KEY;
import static cn.murky.admin.system.api.constant.ErrorConstant.*;

/**
 * i18n Service
 *
 * @auth hans
 */
@Component
public class SysI18nServiceImpl extends ServiceImpl<SysI18nMapper, SysI18n> implements ISysI18nService {
    @Inject
    private ISysDictDataService iSysDictDataService;
    @Inject
    private ISysI18nService iSys18nService;
    @Inject
    private SysI18nMqTemplate sysI18nMqTemplate;
    @Inject
    private RedisClient redisClient;
    private static final String I18N_REDIS_CACHE_KEY = "admin:system:i18n:";
    private static final Cache<String, Map<String,String>> I18N_LOCAL_CACHE_KEY = Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .maximumSize(Long.MAX_VALUE)
            .build();

    /**
     * 根据字典和tag数据节后的动态分页
     *
     * @return 返回的是根据字典内容指定的Map
     */
    @Override
    public Page<Map> page(SysI18nDTO sysI18nDTO) {
        List<String> i18nDict = iSysDictDataService.getI18nDict().stream()
                .map(SysDictDataBO::getDictValue).toList();
        SysI18nPageQuery sysI18nPageQuery = new SysI18nPageQuery().setSysI18nDTO(sysI18nDTO).setI18nKeys(i18nDict);
        return mapper.page(sysI18nPageQuery);
    }

    @Override
    public void refresh() {
        List<SysDictDataBO> i18nDict = iSysDictDataService.getI18nDict();
        for (SysDictDataBO dictTag : iSysDictDataService.getDict(I18N_TAG_DICT_KEY)) {
            for (SysDictDataBO dictLanguage : i18nDict) {
                redisClient.getHash(STR."\{I18N_REDIS_CACHE_KEY}\{dictTag.getDictValue()}:\{dictLanguage.getDictValue()}").clear();
            }
        }
        sysI18nMqTemplate.publish();
    }

    @Override
    public void refreshLocal() {
        I18N_LOCAL_CACHE_KEY.invalidateAll();
    }

    /**
     * 根据i18nKey查询某一个国际化编码的详情信息
     *
     * @return 返回的是根据字典内容指定的字段已经详细信息
     */
    @Override
    public SysI18nVO info(SysI18nDTO sysI18nDTO) {
        List<SysDictDataBO> i18nDict = iSysDictDataService.getI18nDict();
        List<String> i18nDictStrList = i18nDict.stream().map(SysDictDataBO::getDictValue).toList();
        SysI18nPageQuery sysI18nPageQuery = new SysI18nPageQuery().setSysI18nDTO(sysI18nDTO).setI18nKeys(i18nDictStrList);
        SysI18nVO info = mapper.info(sysI18nPageQuery);
        if(info == null){
            return null;
        }
        for (SysDictDataBO sysDictData : i18nDict) {
            boolean have = false;
            for (SysI18nVO.I18nInput i18nInput : info.getI18nInputs()) {
                if (i18nInput.getLanguage().equals(sysDictData.getDictValue())) {
                    have = true;
                    break;
                }
            }
            if (!have) {
                info.pushI18nInputs(sysDictData);
            }
        }
        return info;
    }

    /**
     * 重载保存方法
     *
     * @return 保存状态
     */
    @Tran
    @Override
    public boolean save(SysI18nFromDTO sysI18nFromDTO) {
        long l = mapper.selectByKeyAndExceptTag(sysI18nFromDTO.getI18nKey(), sysI18nFromDTO.getI18nTag());
        // 校验非公共标签是否重复
        if (l > 0) {
            throw new ServiceException(I18N_KEY_ALREADY);
        }
        List<SysDictDataBO> i18nDict = iSysDictDataService.getI18nDict();
        Map<String, String> i18nMap = i18nDict.stream()
                .collect(Collectors.toMap(SysDictDataBO::getDictValue,
                        SysDictDataBO::getDictValue));
        SysDictDataBO defaultI18n = i18nDict.getFirst();
        List<SysI18n> sysI18ns = new ArrayList<>();
        for (SysI18nFromDTO.I18nInput i18nInput : sysI18nFromDTO.getI18nInputs()) {
            Optional.ofNullable(i18nMap.get(i18nInput.getLanguage())).orElseThrow(() -> new ServiceException(ILLEGAL_LANGUAGE));
            if (i18nInput.getI18nValue().equals(defaultI18n.getDictValue())) {
                Optional.of(i18nInput.getI18nValue()).orElseThrow(() -> new ServiceException(DEFAULT_LANGUAGE_NOT_SET));
            }
            SysI18n sysI18n = new SysI18n()
                    .setI18nTag(sysI18nFromDTO.getI18nTag())
                    .setI18nKey(sysI18nFromDTO.getI18nKey())
                    .setI18nValue(i18nInput.getI18nValue())
                    .setLanguage(i18nInput.getLanguage());
            sysI18ns.add(sysI18n);
        }
        boolean b = iSys18nService.saveBatch(sysI18ns);
        if (b) {
            refresh();
            refreshLocal();
        }
        return b;
    }

    /**
     * 重写修改方法
     *
     * @return 保存状态
     */
    @Tran
    @Override
    public boolean edit(SysI18nFromDTO sysI18nFromDTO) {
        List<SysDictDataBO> i18nDict = iSysDictDataService.getI18nDict();
        Map<String, String> i18nMap = i18nDict.stream().collect(Collectors.
                toMap(SysDictDataBO::getDictValue, SysDictDataBO::getDictValue));
        SysDictDataBO defaultI18n = i18nDict.getFirst();
        List<SysI18n> list = new ArrayList<>();
        for (SysI18nFromDTO.I18nInput i18nInput : sysI18nFromDTO.getI18nInputs()) {
            Optional.ofNullable(i18nMap.get(i18nInput.getLanguage())).orElseThrow(() -> new ServiceException(ILLEGAL_LANGUAGE));
            if (i18nInput.getLanguage().equals(defaultI18n.getDictValue())) {
                Optional.ofNullable(i18nInput.getI18nValue()).orElseThrow(() -> new ServiceException(DEFAULT_LANGUAGE_NOT_SET));
            }
            SysI18n sysI18n = new SysI18n()
                    .setId(i18nInput.getId())
                    .setI18nTag(sysI18nFromDTO.getI18nTag())
                    .setI18nKey(sysI18nFromDTO.getI18nKey())
                    .setI18nValue(i18nInput.getI18nValue())
                    .setLanguage(i18nInput.getLanguage());
            list.add(sysI18n);
        }
        boolean b = iSys18nService.saveOrUpdateBatch(list);
        if (b) {
            refresh();
            refreshLocal();
        }
        return b;
    }

    /**
     * 重写删除方法
     * 删除不需要通知租户端进行更新,下次启动自动同步,只需要修改管理端缓存
     *
     * @return 保存状态
     */
    @Override
    public boolean remove(String i18nKey, I18nTag i18nTag) {
        // 删除
        boolean b = mapper.deleteByI18nKeyAndTag(i18nKey,i18nTag) > 0;
        if (b) {
            refresh();
            refreshLocal();
        }
        return b;
    }

    /**
     * 2层缓存 现查本地在查redis因为数据量可能很大
     *
     * @param i18nTagList  标签
     * @param language 语言
     */
    @Override
    public Map<String, String> language(List<String> i18nTagList, String language) {
        HashMap<String, String> result = new HashMap<>();
        if(CollectionUtils.isNotEmpty(i18nTagList)){
            for (String tag : i18nTagList) {
                result.putAll(language(tag, language));
            }
        }
        return result;
    }

    /**
     * 2层缓存 现查本地在查redis因为数据量可能很大
     *
     * @param i18nTag  标签
     * @param language 语言
     */
    @Override
    public Map<String, String> language(String i18nTag, String language) {
        String cacheKey = STR."\{i18nTag}:\{language}";
        // 查询本地缓存,本地缓存持续保存10分钟
        ConcurrentMap<String, Map<String, String>> map = I18N_LOCAL_CACHE_KEY.asMap();
        if (CollectionUtils.isNotEmpty(map.keySet()) && map.containsKey(cacheKey)) {
            return map.get(cacheKey);
        }
        // 本地缓存不存在查询redis缓存
        RedisHash redisHash = redisClient.getHash(I18N_REDIS_CACHE_KEY + cacheKey);
        // 如果redis缓存不存在则查询数据库并存入缓存
        if (redisHash.isEmpty()) {
            Map<String, String> node = new HashMap<>();
            List<SysI18n> sysI18ns = mapper.selectByLanguageAndTag(i18nTag, language);
            for (SysI18n sysI18n : sysI18ns) {
                node.put(sysI18n.getI18nKey(), sysI18n.getI18nValue());
            }
            if(CollectionUtils.isEmpty(node)){
                return node;
            }
            // 更新redis缓存
            redisHash.putAll(node);
            // 更新本地缓存
            I18N_LOCAL_CACHE_KEY.put(cacheKey, node);
            return node;
        }
        // 更新本地缓存
        I18N_LOCAL_CACHE_KEY.put(cacheKey, redisHash);
        return redisHash;
    }
}
